package com.mebs.model.inventory;

import com.mebs.model.Warehouse;
import com.mebs.model.logistics.BoxItem;
import com.mebs.model.CurrencyName;

import javax.persistence.*;

import java.util.Set;
import java.util.Map;
import java.util.HashMap;

import org.jt.jet.util.JetUtils;
import static org.hibernate.proxy.HibernateProxyHelper.getClassWithoutInitializingProxy;

/**
 * Created by Sergey Prasolov, sap@jensentechnologies.com Date: Feb 13, 2006, 4:58:17 PM
 */
@Entity()
@Table(name = "ORDER_ITEM")
public class OrderItem {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "Id")
    private Long id;

    @ManyToOne(optional = false)
    @JoinColumn(name = "OrderId")
    private PurchaseOrder order;

	@Column(name = "CommodityDescription", nullable = true, length = 1024)
    private String commodityDescription;

	@Column(name = "SerialNumber", nullable = true, length = 1024)
	private String serialNumber;

	@Column(name = "PartNumber", nullable = true, length = 1024)
	private String partNumber;

	@Column(name = "LineNumber", nullable = false)
    private Integer lineNumber;

    @Column(name = "QuantityOrdered", nullable = true)
    private Integer quantityOrdered;

    @Column(name = "UnitPrice", nullable = true)
    private Double unitPrice;

	@ManyToOne(optional = true)
    @JoinColumn(name = "CurrencyId")
    private CurrencyName currency;

	@Column(name = "MNumber", nullable = true, unique = true)
	private String mNumber;

	@Column(name = "packedByMebs")
	private Boolean packedByMebs;

    @ManyToOne(optional = true)
    @JoinColumn(name = "WarehouseId")
    private Warehouse warehouse;

	@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
    @JoinColumn(name = "OrderItemId")
    @OrderBy("confirmation,lineNumber")
    private Set<ConfirmationItem> confirmationItems;

    @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
    @JoinColumn(name = "OrderItemId")
    @OrderBy("name")
    private Set<BoxSubItem> subItems;

    @OneToMany(fetch = FetchType.LAZY)
    @JoinColumn(name = "OrderItemId")
    @OrderBy("box")
    private Set<BoxItem> boxItems;
    
    @Column(name = "Eccn", nullable = true)
    private String eccn;
    
    @Column(name = "SchedB", nullable = true)
    private String schedB;
    
    @Column(name = "Hts", nullable = true)
    private String hts;
    
    @Column(name = "Weight", nullable = true)
	private Double weight;

	@Column(name = "WeightUnit", nullable = true)
	private Integer weightUnit;


    /**
     * ********************** Getters-setters ***********************
     */
    public Long getId()
    {
        return id;
    }

    public void setId(Long id)
    {
        this.id = id;
    }

    public PurchaseOrder getOrder()
    {
        return order;
    }

    public void setOrder(PurchaseOrder order)
    {
        this.order = order;
    }

    public String getCommodityDescription()
    {
        return commodityDescription;
    }

    public void setCommodityDescription(String commodityDescription)
    {
        this.commodityDescription = commodityDescription;
    }

	public String getSerialNumber() {
		return serialNumber;
	}

	public void setSerialNumber(String serialNumber) {
		this.serialNumber = serialNumber;
	}

	public String getPartNumber() {
		return partNumber;
	}

	public void setPartNumber(String partNumber) {
		this.partNumber = partNumber;
	}

	public Integer getLineNumber()
    {
        return lineNumber;
    }

    public void setLineNumber(Integer lineNumber)
    {
        this.lineNumber = lineNumber;
    }

    public Integer getQuantityOrdered()
    {
        return quantityOrdered;
    }

    public void setQuantityOrdered(Integer quantityOrdered)
    {
        this.quantityOrdered = quantityOrdered;
    }

    public Double getUnitPrice()
    {
        return unitPrice;
    }

    public void setUnitPrice(Double unitPrice)
    {
        this.unitPrice = unitPrice;
    }

	public CurrencyName getCurrency()
    {
        return currency;
    }

    public void setCurrency(CurrencyName currency)
    {
        this.currency = currency;
    }

	public String getMNumber() {
		return mNumber;
	}

	public void setMNumber(String mNumber) {
		this.mNumber = mNumber;
	}

	public Boolean getPackedByMebs() {
		return packedByMebs;
	}

	public void setPackedByMebs(Boolean packedByMebs) {
		this.packedByMebs = packedByMebs;
	}

    public Warehouse getWarehouse()
    {
        return warehouse;
    }

    public void setWarehouse(Warehouse warehouse)
    {
        this.warehouse = warehouse;
    }

    public Set<ConfirmationItem> getConfirmationItems()
    {
        return confirmationItems;
    }

    public void setConfirmationItems(Set<ConfirmationItem> confirmationItems)
    {
        this.confirmationItems = confirmationItems;
    }

    public Set<BoxSubItem> getSubItems()
    {
        return subItems;
    }

    public void setSubItems(Set<BoxSubItem> subItems)
    {
        this.subItems = subItems;
    }

    public Set<BoxItem> getBoxItems()
    {
        return boxItems;
    }

    public void setBoxItems(Set<BoxItem> boxItems)
    {
        this.boxItems = boxItems;
    }

    public String getEccn() {
		return eccn;
	}

	public void setEccn(String eccn) {
		this.eccn = eccn;
	}

	public String getSchedB() {
		return schedB;
	}

	public void setSchedB(String schedB) {
		this.schedB = schedB;
	}

	public String getHts() {
		return hts;
	}

	public void setHts(String hts) {
		this.hts = hts;
	}
	
	public Double getWeight() {
		return weight;
	}

	public void setWeight(Double weight) {
		this.weight = weight;
	}

	public Integer getWeightUnit() {
		return weightUnit;
	}

	public void setWeightUnit(Integer weightUnit) {
		this.weightUnit = weightUnit;
	}

	@Transient
    public int getBoxItemsSize()
    {
        if (this.subItems == null) {
            return 0;
        } else {
            return this.subItems.size();
        }
    }

    @Transient
    public Integer getBoxedQuantity()
    {
        if (JetUtils.isEmpty(boxItems)) return 0;

        // will count
        int qty = 0;

        // will count subItems separately
        Map<BoxSubItem, Integer> subs = new HashMap<BoxSubItem, Integer>();
        if (!JetUtils.isEmpty(this.getSubItems())) for (BoxSubItem si : this.getSubItems()) subs.put(si, 0);

        // count
        for (BoxItem bi : boxItems) {
            if (bi.getSubItem() == null) qty += bi.getQuantity();
            else subs.put(bi.getSubItem(), bi.getQuantity() + subs.get(bi.getSubItem()));
        }

        // correct
        if (!JetUtils.isEmpty(this.getSubItems())) {
            Integer min = null;
            for (Integer i : subs.values()) {
                if (min == null) min = i;
                else if (i < min) min = i;
            }
            qty += min;
        }

        return qty;
    }

    @Transient
    public Integer getNotBoxedQuantity()
    {
//        return getQuantityOrdered() - getBoxedQuantity();
        return getReceivedQuantity() - getBoxedQuantity();
    }

    @Transient
    public boolean isDeletable()
    {
        return (JetUtils.isEmpty(this.confirmationItems)
            && JetUtils.isEmpty(this.boxItems)
            && JetUtils.isEmpty(this.subItems)); // todo sap delete subitems cascaded
    }

    @Transient
    public Double getTotalPrice()
    {
        return getUPrice() * getQtyOrdered() == 0 ? null : getUPrice() * getQtyOrdered();
    }

	@Transient
	public Double getUPrice()
	{
		return this.unitPrice == null ? 0.0 : this.unitPrice;
	}

	@Transient
    public int getReceivedQuantity()
    {
        if (confirmationItems == null) return 0;

        int summaryQuantity = 0;
        for (ConfirmationItem ci : confirmationItems) {
            summaryQuantity += ci.getQuantityReceived();
        }
        return summaryQuantity;
    }

    @Transient
    public int getUndelivered()
    {

		return getQtyOrdered() - getReceivedQuantity();
    }

    @Transient
    public int getOverShort()
    {
		return getReceivedQuantity() - getQtyOrdered();
    }

	@Transient
	public int getQtyOrdered()
	{
		return quantityOrdered == null ? 0 : quantityOrdered;
	}
	
	@Transient
    public boolean getConfirmed() {
		return !getConfirmationItems().isEmpty();
	}
	
	@Override
    public String toString()
    {
        return this.lineNumber.toString();
    }

    @Override
    public int hashCode()
    {
        if (id == null) return super.hashCode();
        return id.hashCode();
    }

    @Override
    public boolean equals(Object o)
    {
        if (o == null) return false;
        if (this == o) return true;
        if (id == null) return false;
        if (getClassWithoutInitializingProxy(this) != getClassWithoutInitializingProxy(o))
            return false;
        final OrderItem that = (OrderItem) o;
        return id.equals(that.getId());
    }

}
